#!BPY
# encoding: utf-8

"""
Name: 'KISS CM3D model (.anz)'
Blender: 249
Group: 'Import'
Tooltip: 'CM3D .anz importer'
"""

__author__ = 'Desuwa'
__version__ = '0.4.0'
__url__ = "http://code.google.com/p/anz2blend/"
__email__ = "desuwane@gmail.com"

"""
The MIT License

Copyright (c) 2011 Desuwa

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""

import Blender
import bpy
import os
from Blender import BGL, Ipo, Modifier
from Blender.BGL import *
from struct import unpack

Vector = Blender.Mathutils.Vector
Matrix = Blender.Mathutils.Matrix
Euler = Blender.Mathutils.Euler
Quaternion = Blender.Mathutils.Quaternion
ScaleMatrix = Blender.Mathutils.ScaleMatrix
RotationMatrix = Blender.Mathutils.RotationMatrix
TranslationMatrix = Blender.Mathutils.TranslationMatrix

#----------------
# BinaryReader
#----------------

def readSingle(r):
	data = r.read(4)
	return float(unpack("<f", data)[0]) if data != "" else False

def readInt(r):
	data = r.read(4)
	return int(unpack("<i", data)[0]) if data != "" else False

def readString(r):
	l = readInt(r)
	if l == "": return False
	data = r.read(l)
	return "".join(unpack("<%ds" % l, data)) if data != "" else False

#----------------
# Anz
#----------------
BODY_ARMATURE_NAME = "BodyArmature"
BODY_ARMATURE_BONES = 108

ANIMFPS = 30

MATNAME_MIN_LEN = 8

class MorphVertex():
	index = 0
	x = y = z = 0
	
	def __str__(self):
		return "%d: (%f %f %f)" % (self.index, self.x, self.y, self.z)

class AnzVertex():
	x = y = z = nx = ny = nz = tu = tv = 0
	bones = []
	weights = []
	m = 0
	
	def __str__(self):
		return "%f %f %f | %f %f %f | %f %f\n%s\n%s"\
		% (self.x, self.y, self.z, self.nx, self.ny, self.nz,\
		self.tu, self.tv, self.bones, self.weights)
	
	def __eq__(self, v):
		return self.x == v.x and self.y == v.y and self.z == v.z \
		and self.nx == v.nx and self.ny == v.ny and self.nz == v.nz \
		and self.tu == v.tu and self.tv == v.tv \
		and self.bones == v.bones and self.weights == v.weights \
		and self.m == v.m
	
	def __hash__(self):
		h = hash(self.x) ^ hash(self.y) ^ hash(self.z)\
		^ hash(self.nx) ^ hash(self.ny) ^ hash(self.nz) ^ hash(self.m)\
		^ hash(self.tu) ^ hash(self.tv)
		for b in self.bones:
			h ^= hash(b)
		for w in self.weights:
			h ^= hash(w)
		return h

def readMaterials(r):
	materials = []
	materialCount = readInt(r)
	print "Materials: %d" % materialCount
	for i in xrange(0, materialCount):
		mat = {}
		mat["name"] = readString(r)
		mat["texscaleX"] = readSingle(r)
		mat["texscaleY"] = readSingle(r)
		r.read(12)
		mat["dispMode"] = readInt(r)
		r.read(8)
		mat["texId"] = readInt(r)
		materials.append(mat)
	return materials

def readTextures(count, r):
	textures = []
	for i in xrange(0, count):
		length = readInt(r)
		if length > 0:
			textures.append(r.read(length))
		else:
			textures.append(None)
	return textures

def readBones(r):
	bones = []
	boneCount = readInt(r)
	print "Bone count: %d" % boneCount
	for i in xrange(0, boneCount):
		bone = {}
		bone["name"] = readString(r)
		bone["parent"] = readInt(r)
		r.read(12)
		bone["rest"] = [] # used for the animations
		for i in xrange(0, 9):
			bone["rest"].append(readSingle(r)) # not useful for now 72
		r.read(88)
		bone["matrix"] = Matrix(\
		[readSingle(r), readSingle(r), readSingle(r), readSingle(r)],\
		[readSingle(r), readSingle(r), readSingle(r), readSingle(r)],\
		[readSingle(r), readSingle(r), readSingle(r), readSingle(r)],\
		[readSingle(r), readSingle(r), readSingle(r), readSingle(r)],)
		r.read(256) # matrixes
		r.read(5) # flags
		bones.append(bone)
	return bones

def readAnimations(r):
	animations = []
	animationCount = readInt(r)
	print "Animation count: %d" % animationCount
	for i in xrange(0, animationCount):
		animation = {}
		animation["type"] = readInt(r)
		if (animation["type"] == 0):
			animations.append(animation)
			continue
		r.read(readInt(r) * 4)
		if (animation["type"] == 1): animation["frameCount"] = readInt(r)
		else: animation["frameCount"] = 0
		animation["objects"] = []
		boneCount = readInt(r)
		for j in xrange(0, boneCount):
			bone = {}
			if (animation["type"] == 1): bone["name"] = readString(r)
			bone["num"] = readInt(r)
			bone["SRT"] = []
			SRTSize = (9 if (animation["type"] == 1) else 2)
			for k in xrange(0, SRTSize):
				data = {}
				data["flag"] = r.read(1)
				data["value"] = readSingle(r)
				data["keyFrameCount"] = readInt(r)
				if data["flag"] == "\x01":
					data["keyFrames"] = []
					for l in xrange(0, data["keyFrameCount"]):
						kf = {}
						kf["num"] = int(readSingle(r))
						kf["value"] = readSingle(r)
						kf["tangent"] = readSingle(r)
						data["keyFrames"].append(kf)
				bone["SRT"].append(data)
			animation["objects"].append(bone)
		animation["footer"] = readInt(r)
		animations.append(animation)
		
	return animations

def builBoneAnimations(armObj, animations):
	# Everything is very approximative
	print "Generating skeletal animations"
	
	rotMat = Euler(90, 0, 0).toMatrix().resize4x4()
	arm = armObj.getData()
	pose = armObj.getPose()
	
	scn = bpy.data.scenes.active
	cntxt = scn.getRenderingContext()
	cntxt.fps = ANIMFPS
	
	interp = Blender.IpoCurve.InterpTypes.LINEAR
	posesize = [Blender.Object.Pose.SIZE]
	poserot = [Blender.Object.Pose.ROT]
	poseloc = [Blender.Object.Pose.LOC]
	
	driver = scn.objects.new("Empty", "driver")
	
	for anim in animations:
		if anim["type"] > 1:
			continue
		
		if anim["type"] == 0:
			bpy.data.actions.new(armObj.name + ".Dummy")
			continue
			
		action = bpy.data.actions.new(armObj.name)
		action.setActive(armObj)
		
		for channel in anim["objects"]:
			
			if pose.bones[channel["name"]] is None:
				continue
			
			#driver = scn.objects.new("Empty", channel["name"])
			driverIpo = bpy.data.ipos.new(channel["name"], "Object")
			driver.setIpo(driverIpo)
			
			posebone = pose.bones[channel["name"]]
			posebone.insertKey(armObj, 0, posesize)
			ipo = action.getChannelIpo(channel["name"])
			ipo[Ipo.PO_SCALEX] = None
			ipo[Ipo.PO_SCALEY] = None
			ipo[Ipo.PO_SCALEZ] = None
			
			if posebone.parent is not None:
				restPMat = arm.bones[channel["name"]].parent.matrix["ARMATURESPACE"].copy().invert()
				hasParent = True
			else:
				restPMat = rotMat
				hasParent = False
			# to parent rest matrix
			restMat = arm.bones[channel["name"]].matrix["ARMATURESPACE"] * restPMat
			# to parent rest position
			restTrans = restMat.translationPart()
			
			# rotation
			kflist = {}
			# X
			rotIcu = driverIpo.addCurve("RotX")
			rotIcu.interpolation = interp
			if channel["SRT"][3]["flag"] == "\x01":
				for kf in channel["SRT"][3]["keyFrames"]:
					frame = int(kf["num"]) + 1
					rotIcu.append( (frame, kf["value"] / 10.0) )
					kflist[frame] = True
			else:
				val = channel["SRT"][3]["value"] / 10.0
				rotIcu.append( (1, val) )
				rotIcu.append( (anim["frameCount"], val) )
				kflist[1] = kflist[anim["frameCount"]] = True
			# Y
			rotIcu = driverIpo.addCurve("RotY")
			rotIcu.interpolation = interp
			if channel["SRT"][4]["flag"] == "\x01":
				for kf in channel["SRT"][4]["keyFrames"]:
					frame = int(kf["num"]) + 1
					rotIcu.append( (frame, kf["value"] / 10.0) )
					kflist[frame] = True
			else:
				val = channel["SRT"][4]["value"] / 10.0
				rotIcu.append( (1, val) )
				rotIcu.append( (anim["frameCount"], val) )
				kflist[1] = kflist[anim["frameCount"]] = True
			# Z
			rotIcu = driverIpo.addCurve("RotZ")
			rotIcu.interpolation = interp
			if channel["SRT"][5]["flag"] == "\x01":
				for kf in channel["SRT"][5]["keyFrames"]:
					frame = int(kf["num"]) + 1
					rotIcu.append( (frame, kf["value"] / 10.0) )
					kflist[frame] = True
			else:
				val = channel["SRT"][5]["value"] / 10.0
				rotIcu.append( (1, val) )
				rotIcu.append( (anim["frameCount"], val) )
				kflist[1] = kflist[anim["frameCount"]] = True
			
			# applying rotation to bone
			for kf in kflist.keys():
				cntxt.cFrame = kf
				armObj.evaluatePose(kf)
				rot = driver.getMatrix()
				bTransMat = TranslationMatrix(posebone.poseMatrix.translationPart())
				if hasParent is True:
					pRotMat = posebone.parent.poseMatrix.rotationPart().resize4x4()
					mat = rot * pRotMat * bTransMat
				else:
					mat = rot * restPMat * bTransMat
				posebone.poseMatrix = mat
				posebone.insertKey(armObj, kf, poserot)
			
			ipo[Ipo.PO_QUATW].interpolation = interp
			ipo[Ipo.PO_QUATX].interpolation = interp
			ipo[Ipo.PO_QUATY].interpolation = interp
			ipo[Ipo.PO_QUATZ].interpolation = interp
			
			# translation
			kflist = {}
			# X
			locIcu = driverIpo.addCurve("LocX")
			locIcu.interpolation = interp
			if channel["SRT"][6]["flag"] == "\x01":
				for kf in channel["SRT"][6]["keyFrames"]:
					frame = int(kf["num"]) + 1
					locIcu.append( (frame, kf["value"] / SCALEFACTOR) )
					kflist[frame] = True
			else:
				val = channel["SRT"][6]["value"] / SCALEFACTOR
				locIcu.append( (1, val) )
				locIcu.append( (anim["frameCount"], val) )
				kflist[1] = kflist[anim["frameCount"]] = True
			
			# Y
			locIcu = driverIpo.addCurve("LocY")
			locIcu.interpolation = interp
			if channel["SRT"][7]["flag"] == "\x01":
				for kf in channel["SRT"][7]["keyFrames"]:
					frame = int(kf["num"]) + 1
					locIcu.append( (frame, kf["value"] / SCALEFACTOR) )
					kflist[frame] = True
			else:
				val = channel["SRT"][7]["value"] / SCALEFACTOR
				locIcu.append( (1, val) )
				locIcu.append( (anim["frameCount"], val) )
				kflist[1] = kflist[anim["frameCount"]] = True
			
			# Z
			locIcu = driverIpo.addCurve("LocZ")
			locIcu.interpolation = interp
			if channel["SRT"][8]["flag"] == "\x01":
				for kf in channel["SRT"][8]["keyFrames"]:
					frame = int(kf["num"]) + 1
					locIcu.append( (frame, kf["value"] / SCALEFACTOR) )
					kflist[frame] = True
			else:
				val = channel["SRT"][8]["value"] / SCALEFACTOR
				locIcu.append( (1, val) )
				locIcu.append( (anim["frameCount"], val) )
				kflist[1] = kflist[anim["frameCount"]] = True
			
			# applying translation to bone
			for kf in kflist.keys():
				cntxt.cFrame = kf
				armObj.evaluatePose(kf)
				if hasParent is True:
					mat = driver.getMatrix() * posebone.parent.poseMatrix
				else:
					mat = driver.getMatrix()
				mat = TranslationMatrix(mat.translationPart())
				posebone.poseMatrix = posebone.poseMatrix.rotationPart().resize4x4() * mat
				posebone.insertKey(armObj, kf, poseloc)
			
			ipo[Ipo.PO_LOCX].interpolation = interp
			ipo[Ipo.PO_LOCY].interpolation = interp
			ipo[Ipo.PO_LOCZ].interpolation = interp
			
			driver.clearIpo()
			driver.setLocation(0, 0, 0)
			driver.setEuler( [0, 0, 0] )
			
	scn.objects.unlink(driver)
	cntxt.currentFrame(1)

def makeLocCurve(frames, icu, axis, hasParent):
	for kf in frames:
		frame = int(kf["num"]) + 1
		if hasParent is True:
			armObj.evaluatePose(frame)
			pTrans = pose.bones[channel["name"]].parent.poseMatrix.translationPart()
		else:
			pTrans = Vector()
		icu.append( (frame, pTrans[axis] + kf["value"]) )

def readMeshes(r):
	meshes = []
	meshCount = readInt(r)
	print "Mesh count: %d" % meshCount
	for i in xrange(0, meshCount):
		mesh = {}
		mesh["id"] = i
		mesh["indexes"] = []
		mesh["matId"] = readInt(r)
		vMap = {}
		bonecount = readInt(r)
		mesh["bones"] = []
		if bonecount > 0:
			for j in xrange(0, bonecount):
				mesh["bones"].append(readInt(r))
		mesh["nativelength"] = readInt(r)
		mesh["vertices"] = []
		for j in xrange(0, mesh["nativelength"]):
			v = AnzVertex()
			v.x = readSingle(r)
			v.y = readSingle(r)
			v.z = readSingle(r)
			v.nx = readSingle(r)
			v.ny = readSingle(r)
			v.nz = readSingle(r)
			v.tu = readSingle(r)
			v.tv = readSingle(r)
			v.bones = []
			v.weights = []
			for k in xrange(0, 4):
				v.bones.append(readInt(r))
			for k in xrange(0, 4):
				v.weights.append(readInt(r))
			v.m = readInt(r)
			if vMap.has_key(v):
				mesh["indexes"].append(vMap[v])
			else:
				index = len(mesh["vertices"])
				vMap[v] = index
				mesh["vertices"].append(v)
				mesh["indexes"].append(index)
		r.read(5) # flags
		meshes.append(mesh)
	return meshes

def readMorphs(r):
	r.read(12)
	morphs = []
	while 1:
		n = readInt(r)
		if n == False: break
		morph = []
		for i in xrange(0, n):
			v = MorphVertex()
			v.index = readInt(r)
			v.x = readSingle(r)
			v.y = readSingle(r)
			v.z = readSingle(r)
			morph.append(v)
		morphs.append(morph)
	return morphs

def makeName(path, splitExt = False):
	f = Blender.sys.basename(path)
	if splitExt: f = Blender.sys.splitext(f)[0]
	return f

def writeTextures(textures, materials):
	global srcAnz, texOut, MAKEFOLDER
	if texOut == "":
		dirname = Blender.sys.dirname(srcAnz)
	else:
		dirname = texOut
	if MAKEFOLDER:
		dirname = Blender.sys.join(dirname, makeName(srcAnz, True))
	if not os.path.exists(dirname):
		os.makedirs(dirname)	
	for mat in materials:
		filename = Blender.sys.basename(mat["name"])
		if len(filename) < MATNAME_MIN_LEN:
			print "Warning: file name too short, padding with zeroes (%s)" % filename
			file, ext = Blender.sys.splitext(filename)
			filename = file + "".zfill(MATNAME_MIN_LEN - len(filename)) + ext
		dest = Blender.sys.join(dirname, filename)
		if textures[mat["texId"]]:
			with open(dest, "wb") as w:
				w.write(textures[mat["texId"]])
		mat["texFile"] = dest

#----------------
# GUI
#----------------
texOut = Blender.Draw.Create("")
srcAnz = Blender.Draw.Create("")
SCALEFACTOR = Blender.Draw.Create(10.0)
ADDARMATURE = Blender.Draw.Create(0)
BONEANIMS = Blender.Draw.Create(0)
ADDMORPHS = Blender.Draw.Create(1)
MAKEFOLDER = Blender.Draw.Create(1)

def getTexOut(path):
	global texOut
	texOut.val = Blender.sys.dirname(path)
	Blender.Draw.Redraw(1)

def getSrcAnz(path):
	global srcAnz, texOut
	srcAnz.val = path
	if texOut.val == "":
		texOut.val = Blender.sys.dirname(path)
	Blender.Draw.Redraw(1)

def guiEvent(event, value):	
	if event == Blender.Draw.ESCKEY:
		Blender.Draw.Exit()
		return

def guiButton(event):
	global srcAnz, ADDVGROUPS, ADDARMATURE, BONEANIMS
	if event == 11:
		Blender.Window.FileSelector(getSrcAnz, "Import")
	elif event == 21:
		Blender.Window.FileSelector(getTexOut, "Select")
	elif event == 5:
		if ADDARMATURE.val == 0:
			BONEANIMS.val = 0
	elif event == 6:
		if BONEANIMS.val == 1:
			ADDARMATURE.val = 1
	elif event == 666:
		if srcAnz.val == "": return
		importAnz()
		Blender.Draw.Exit()
		Blender.Window.RedrawAll()
	elif event == 999:
		Blender.Draw.Exit()
	
	Blender.Draw.Redraw(-1)

def drawGui():
	global srcAnz, texOut, SCALEFACTOR, ADDARMATURE, BONEANIMS, ADDMORPHS, MAKEFOLDER
	
	glClearColor(0.55, 0.57, 0.67, 1)
	glClear(BGL.GL_COLOR_BUFFER_BIT)
	
	glColor3f(0.70, 0.71, 0.85)
	glRasterPos2i(35, 40)
	Blender.Draw.Text("KISS ANZ Importer " + __version__, "large")
	glRasterPos2i(35, 25)
	Blender.Draw.Text(__url__, "small")
	
	srcAnz = Blender.Draw.String("ANZ: ", 1, 35, 275, 315, 18, srcAnz.val, 255)
	Blender.Draw.PushButton("Browse", 11, 355, 275, 55, 18, "")
	
	texOut = Blender.Draw.String("Unpack to: ", 2, 35, 250, 315, 18, texOut.val, 255,
		"Textures output path. Defaults to the same directory as the ANZ file")
	Blender.Draw.PushButton("Browse", 21, 355, 250, 55, 18, "")
	MAKEFOLDER = Blender.Draw.Toggle("Create folder", 22, 415, 250, 80, 18, MAKEFOLDER.val)
	
	glColor3f(0.16, 0.168, 0.2)
	
	SCALEFACTOR = Blender.Draw.Number("Scale:", 3, 35, 220, 150, 18, SCALEFACTOR.val, 0, 100)
	glRasterPos2i(195, 225)
	Blender.Draw.Text("Scale the model by 1 / SCALEFACTOR")
	
	ADDARMATURE = Blender.Draw.Toggle("Armature", 5, 35, 195, 150, 18, ADDARMATURE.val)
	glRasterPos2i(195, 200)
	Blender.Draw.Text("Import bones")
	
	BONEANIMS = Blender.Draw.Toggle("Bone animations", 6, 35, 170, 150, 18, BONEANIMS.val)
	glRasterPos2i(195, 175)
	Blender.Draw.Text("Import bones animations (approximative)")
	
	ADDMORPHS = Blender.Draw.Toggle("Import morphs", 7, 35, 115, 150, 18, ADDMORPHS.val)
	glRasterPos2i(195, 120)
	Blender.Draw.Text("Import morph targets")

	Blender.Draw.PushButton("Import", 666, 35, 70, 75, 30, "")
	Blender.Draw.PushButton("Cancel", 999, 120, 70, 75, 30, "")

#----------------
# Main
#----------------
def importAnz():
	global srcAnz, texOut, SCALEFACTOR, ADDARMATURE, BONEANIMS, ADDMORPHS, MAKEFOLDER
	
	srcAnz = srcAnz.val
	texOut = texOut.val
	SCALEFACTOR = 1.0 if SCALEFACTOR.val == 0 else SCALEFACTOR.val
	ADDARMATURE = ADDARMATURE.val
	BONEANIMS = BONEANIMS.val
	ADDMORPHS = ADDMORPHS.val
	MAKEFOLDER = MAKEFOLDER.val
	
	if not Blender.sys.exists(srcAnz):
		print "File doesn't exist: %s" % srcAnz
		return
	
	print "======= Importing ANZ ======="
	STARTTIME = Blender.sys.time()
	
	Blender.Window.WaitCursor(1)
	
	with open(srcAnz, "rb") as r:
		basename = makeName(srcAnz, True)
		source = readString(r)
		scale = readSingle(r)
		
		materials = readMaterials(r)
		textures = readTextures(len(materials), r)
		writeTextures(textures, materials)
		del textures
		textures = None
		bones = readBones(r)
		animations = readAnimations(r)
		meshes = readMeshes(r)
		if ADDMORPHS:
			morphs = readMorphs(r)	
		else:
			morphs = []
	
	scn = bpy.data.scenes.active
	Blender.Window.EditMode(0)
	
	# ***************	
	# VGroups and Bones
	# ***************
	boneList = []
	boneCount = len(bones)
	if boneCount > 0:
		for b in bones:
			boneList.append(b["name"])
		if ADDARMATURE:
			# assumptions...
			if boneCount == BODY_ARMATURE_BONES:
				armName = BODY_ARMATURE_NAME
			else:
				# for bone animation export compatibility
				if BONEANIMS:
					armName = basename[-21:]
				else:
					armName = basename + "Armature"
			try:
				armObj = bpy.data.objects[armName]
				armData = armObj.getData()
				print "Armature already exists, skipping."
			except:
				print "Creating armature (%d bones)" % len(bones)
				armData = bpy.data.armatures.new(armName)
				armObj = scn.objects.new(armData)
				armData.drawType = Blender.Armature.STICK
				armData.makeEditable()
				scaleMat = ScaleMatrix(1 / SCALEFACTOR, 4)
				rotMat = Euler(90, 0, 0).toMatrix().resize4x4()
				for b in bones:
					bone = Blender.Armature.Editbone()
					bone.name = b["name"]
					
					if b["parent"] > -1:
						bone.parent = armData.bones[boneList[b["parent"]]]
					
					# Fixing axis and scale
					asMat = b["matrix"] * rotMat * scaleMat
					bone.matrix = asMat
					armData.bones[bone.name] = bone
				
				armData.update()
				
				armObj.drawMode = Blender.Object.DrawModes.XRAY
				#armObj.restrictDisplay = True
				
				if BONEANIMS:
					builBoneAnimations(armObj, animations)
	
	# ***************	
	# Building meshes
	# ***************
	morphPos = 0
	for m in meshes:
		meshName = "%s-%d" % (basename.split("_", 1)[0], m["id"])
		m["name"] = meshName
		
		print "Building mesh " + meshName
		print "\tBones: %d" % len(m["bones"])
		print "\tVertices: %d" % len(m["vertices"])
		#print "\tNative vertices: %d" % m["nativelength"]
		
		mesh = bpy.data.meshes.new(meshName)
		
		# Adding material
		texFile = materials[m["matId"]]["texFile"]
		texSize = (materials[m["matId"]]["texscaleX"], materials[m["matId"]]["texscaleY"], 1.0)
		texName = makeName(texFile)
		try:
			image = Blender.Image.Load(texFile)
			image.setName(texName)
		except:
			image = None
		texture = bpy.data.textures.new(texName)
		texture.setImage(image)
		# Display mode flag: 1 = double sided, 2 = inverted, 3 = no change
		if (materials[m["matId"]]["dispMode"] == 1):
			matName = "d-" + texName
		elif (materials[m["matId"]]["dispMode"] == 2):
			matName = "i-" + texName
		else:
			matName = texName
		material = bpy.data.materials.new(matName)
		material.setTexture(0, texture, Blender.Texture.TexCo.UV)
		material.textures[0].size = texSize
		mesh.materials += [material]
		
		# Adding vertices
		mesh.verts.extend(0, 0, 0) # I put on my robe and wizard hat
		for v in m["vertices"]:
			# -z <-> y
			mesh.verts.extend(v.x / SCALEFACTOR, -v.z / SCALEFACTOR, v.y / SCALEFACTOR)
		
		# Adding faces
		for i in xrange(0, len(m["indexes"]) - 2):
			a = m["indexes"][i]
			b = m["indexes"][i + 1]
			c = m["indexes"][i + 2]
			
			if (a == b or b == c or c == a):
				continue
			
			if ((i % 2) == 1):
				b = c;
				c = m["indexes"][i + 1]
			
			fid = mesh.faces.extend(a + 1, b + 1, c + 1, ignoreDups = True, indexList = True)[0]
			mesh.faces[fid].image = image
			mesh.faces[fid].uv = [\
			Vector(m["vertices"][a].tu, 1 - m["vertices"][a].tv),\
			Vector(m["vertices"][b].tu, 1 - m["vertices"][b].tv),\
			Vector(m["vertices"][c].tu, 1 - m["vertices"][c].tv),\
			]
			
		print "\tFaces: %d" % len(mesh.faces)
		mesh.verts.delete([0]) # dummy vertex
		meshObj = scn.objects.new(mesh, meshName)
		mesh.flipNormals()
		
		# Rigging
		if len(boneList) > 0:
			print "Creating vertex groups"
			vGroups = []
			for b in m["bones"]:
				grp = boneList[b]
				mesh.addVertGroup(grp)
				vGroups.append(grp)
			vi = 0
			for v in m["vertices"]:
				for i in xrange(0, 4):
					if v.bones[i] == -1: break
					grp = vGroups[v.bones[i]]
					weight = v.weights[i] / 100.0
					mesh.assignVertsToGroup(\
					grp, [vi], weight, Blender.Mesh.AssignModes.REPLACE)
				vi += 1
			# adding Armature moifier
			if ADDARMATURE:
				mod = meshObj.modifiers.append(Modifier.Types.ARMATURE)
				mod[Modifier.Settings.OBJECT] = armObj
				meshObj.makeDisplayList()
		
		# Building morph targets
		if len(morphs) > 0:
			mId = 0
			for morph in morphs:
				scn.objects.selected = []
				meshObj.sel = 1
				Blender.Object.Duplicate(mesh = 1)
				morphObj = scn.objects.active
				morphMesh = morphObj.getData(mesh = True)
				morphMesh.name = morphObj.name = ("m-%d-" % mId) + mesh.name
				
				mId += 1
				offsets = {}
				
				for offset in morph:
					vi = offset.index - morphPos
					if vi < 0 or vi >= m["nativelength"]: continue
					offsets[m["indexes"][vi]] = offset
				
				vi = 0
				for i in morphMesh.verts:
					if vi in offsets:
						i.co.x += offsets[vi].x / SCALEFACTOR
						i.co.y -= offsets[vi].z / SCALEFACTOR
						i.co.z += offsets[vi].y / SCALEFACTOR
					vi += 1
				
				morphMesh.update()
				
				morphObj.restrictDisplay = True
				
			morphPos += m["nativelength"]
	
	scn.getRenderingContext().cFrame = 1
	scn.objects.active = None
	scn.objects.selected = []
	scn.update()
	Blender.Window.WaitCursor(0)
	print "== Done, took %f seconds ==" % (Blender.sys.time() - STARTTIME)

#----------------
# Here it comes
#----------------
Blender.Draw.Register(drawGui, guiEvent, guiButton)

